package com.dj.product.k2faas.jpa.wrapper;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.dj.product.k2faas.common.exception.K2Exception;
import com.dj.product.k2faas.common.reflection.SerializableFunction;
import com.dj.product.k2faas.common.utils.ReflectionUtil;
import com.dj.product.k2faas.jpa.query.Query;
import com.dj.product.k2faas.jpa.query.Type;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import org.springframework.data.jpa.domain.Specification;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
 * Created by 丁建 on 2025/9/23 09:58
 *
 * @author 丁建
 */
@SuppressWarnings({ "unchecked", "rawtypes", "SizeReplaceableByIsEmpty" })
public abstract class PredicateWrapper<E> {
    boolean andLink = true;

    List<Query> list = new ArrayList<>();

    List<PredicateWrapper<E>> wrappers = new ArrayList<>();

    /**
     * 根据逻辑类型生成Specification实体
     *
     * @return Specification实体
     */
    public Specification<E> build() {

        return (root, query, cb) -> {
            //如果没有条件拼接，直接返回1=1
            if (list.size() == 0 && wrappers.size() == 0) {
                return cb.conjunction();
            }

            var isAllEmpty = list.stream().allMatch(q -> ObjectUtil.isEmpty(q.getVal()));
            if (isAllEmpty && wrappers.size() == 0) {
                return cb.conjunction();
            }

            List<Predicate> predicates = new ArrayList<>(list.size());
            for (Query q : list) {

                Object val = q.getVal();

                //空值且类型不为isNull和notNull,直接跳过
                if (ObjectUtil.isEmpty(val) && (Type.IS_NULL != q.getType() && Type.NOT_NULL != q.getType())) {
                    continue;
                }
                // 模糊多字段
                String[] blurryArray = q.getBlurry();
                if (ObjectUtil.isNotEmpty(blurryArray)) {
                    List<Predicate> orPredicate = new ArrayList<>();
                    for (String blurry : blurryArray) {
                        orPredicate.add(cb.like(root.get(blurry).as(String.class), "%" + val + "%"));
                    }
                    Predicate[] p = new Predicate[orPredicate.size()];
                    predicates.add(cb.or(orPredicate.toArray(p)));
                    continue;
                }

                String propName = q.getPropName();

                switch (q.getType()) {
                case EQUAL:
                    predicates.add(cb.equal(getExpression(propName, root), val));
                    break;
                case GREATER_THAN:
                    predicates.add(cb.greaterThan(getExpression(propName, root), (Comparable) val));
                    break;
                case GREATER_THAN_EQ:
                    predicates.add(cb.greaterThanOrEqualTo(getExpression(propName, root), (Comparable) val));
                    break;
                case LESS_THAN:
                    predicates.add(cb.lessThan(getExpression(propName, root), (Comparable) val));
                    break;
                case LESS_THAN_EQ:
                    predicates.add(cb.lessThanOrEqualTo(getExpression(propName, root), (Comparable) val));
                    break;
                case INNER_LIKE:
                    predicates.add(cb.like(getExpression(propName, root).as(String.class), "%" + val + "%"));
                    break;
                case LEFT_LIKE:
                    predicates.add(cb.like(getExpression(propName, root).as(String.class), "%" + val));
                    break;
                case RIGHT_LIKE:
                    predicates.add(cb.like(getExpression(propName, root).as(String.class), val + "%"));
                    break;
                case IN:
                    if (CollUtil.isNotEmpty((Collection<Object>) val)) {
                        predicates.add(getExpression(propName, root).in((Collection<Object>) val));
                    }
                    break;
                case NOT_IN:
                    if (CollUtil.isNotEmpty((Collection<Object>) val)) {
                        predicates.add(getExpression(propName, root).in((Collection<Object>) val).not());
                    }
                    break;
                case NOT_EQUAL:
                    predicates.add(cb.notEqual(getExpression(propName, root), val));
                    break;
                case NOT_NULL:
                    predicates.add(cb.isNotNull(getExpression(propName, root)));
                    break;
                case IS_NULL:
                    predicates.add(cb.isNull(getExpression(propName, root)));
                    break;
                case BETWEEN:
                    List<Object> between = new ArrayList<>((List<Object>) val);
                    predicates.add(cb.between(getExpression(propName, root), (Comparable) between.get(0), (Comparable) between.get(1)));
                    break;
                default:
                    throw new K2Exception("不支持的操作符：%s".formatted(q.getType()));
                }
            }
            int size = predicates.size();
            if (andLink) {

                for (PredicateWrapper<E> wrapper : wrappers) {
                    Predicate p = wrapper.build().toPredicate(root, query, cb);
                    predicates.add(p);
                }

                return cb.and(predicates.toArray(new Predicate[size]));

            }

            for (PredicateWrapper<E> wrapper : wrappers) {
                predicates.add(cb.or(wrapper.build().toPredicate(root, query, cb)));
            }

            return cb.or(predicates.toArray(new Predicate[size]));
        };
    }

    @SuppressWarnings("unchecked")
    private <T, R> Expression<T> getExpression(String attributeName, Root<R> root) {
        return root.get(attributeName);
    }

    public <R> PredicateWrapper<E> eq(String propName, R value) {
        list.add(Query.build(Type.EQUAL, propName, value));
        return this;
    }

    public <R> PredicateWrapper<E> eq(SerializableFunction<E, R> propName, R value) {
        list.add(Query.build(Type.EQUAL, ReflectionUtil.getFieldName(propName), value));
        return this;
    }

    public <R> PredicateWrapper<E> notEq(SerializableFunction<E, R> propName, R value) {
        list.add(Query.build(Type.NOT_EQUAL, ReflectionUtil.getFieldName(propName), value));
        return this;
    }

    public <R> PredicateWrapper<E> notEq(String propName, R value) {
        list.add(Query.build(Type.NOT_EQUAL, propName, value));
        return this;
    }

    public <R> PredicateWrapper<E> blurry(R value, SerializableFunction<E, R>... propNames) {

        String[] blurry = new String[propNames.length];

        for (int i = 0; i < propNames.length; i++) {
            blurry[i] = ReflectionUtil.getFieldName(propNames[i]);
        }

        list.add(Query.build(blurry, value));
        return this;
    }

    public <R> PredicateWrapper<E> blurry(R value, String... propNames) {

        list.add(Query.build(propNames, value));
        return this;
    }

    public <R> PredicateWrapper<E> gt(SerializableFunction<E, R> propName, Comparable<R> value) {
        list.add(Query.build(Type.GREATER_THAN, ReflectionUtil.getFieldName(propName), value));
        return this;
    }

    public <R> PredicateWrapper<E> gt(String propName, Comparable<R> value) {
        list.add(Query.build(Type.GREATER_THAN, propName, value));
        return this;
    }

    public <R> PredicateWrapper<E> gte(SerializableFunction<E, R> propName, Comparable<R> value) {
        list.add(Query.build(Type.GREATER_THAN_EQ, ReflectionUtil.getFieldName(propName), value));
        return this;
    }

    public <R> PredicateWrapper<E> gte(String propName, Comparable<R> value) {
        list.add(Query.build(Type.GREATER_THAN_EQ, propName, value));
        return this;
    }

    public <R> PredicateWrapper<E> lt(SerializableFunction<E, R> propName, Comparable<R> value) {
        list.add(Query.build(Type.LESS_THAN, ReflectionUtil.getFieldName(propName), value));
        return this;
    }

    public <R> PredicateWrapper<E> lt(String propName, Comparable<R> value) {
        list.add(Query.build(Type.LESS_THAN, propName, value));
        return this;
    }

    public <R> PredicateWrapper<E> lte(SerializableFunction<E, R> propName, Comparable<R> value) {
        list.add(Query.build(Type.LESS_THAN_EQ, ReflectionUtil.getFieldName(propName), value));
        return this;
    }

    public <R> PredicateWrapper<E> lte(String propName, Comparable<R> value) {
        list.add(Query.build(Type.LESS_THAN_EQ, propName, value));
        return this;
    }

    public <R> PredicateWrapper<E> innerLike(SerializableFunction<E, R> propName, String value) {
        list.add(Query.build(Type.INNER_LIKE, ReflectionUtil.getFieldName(propName), value));
        return this;
    }

    public <R> PredicateWrapper<E> innerLike(String propName, String value) {
        list.add(Query.build(Type.INNER_LIKE, propName, value));
        return this;
    }

    public <R> PredicateWrapper<E> leftLike(SerializableFunction<E, R> propName, String value) {
        list.add(Query.build(Type.LEFT_LIKE, ReflectionUtil.getFieldName(propName), value));
        return this;
    }

    public <R> PredicateWrapper<E> leftLike(String propName, String value) {
        list.add(Query.build(Type.LEFT_LIKE, propName, value));
        return this;
    }

    public <R> PredicateWrapper<E> rightLike(SerializableFunction<E, R> propName, String value) {
        list.add(Query.build(Type.RIGHT_LIKE, ReflectionUtil.getFieldName(propName), value));
        return this;
    }

    public <R> PredicateWrapper<E> rightLike(String propName, String value) {
        list.add(Query.build(Type.RIGHT_LIKE, propName, value));
        return this;
    }

    public <R> PredicateWrapper<E> in(SerializableFunction<E, R> propName, List<R> values) {
        list.add(Query.build(Type.IN, ReflectionUtil.getFieldName(propName), values));
        return this;
    }

    public <R> PredicateWrapper<E> in(String propName, List<R> values) {
        list.add(Query.build(Type.IN, propName, values));
        return this;
    }

    public <R> PredicateWrapper<E> notIn(SerializableFunction<E, R> propName, List<R> values) {
        list.add(Query.build(Type.NOT_IN, ReflectionUtil.getFieldName(propName), values));
        return this;
    }

    public <R> PredicateWrapper<E> notIn(String propName, List<R> values) {
        list.add(Query.build(Type.NOT_IN, propName, values));
        return this;
    }

    public <R> PredicateWrapper<E> between(SerializableFunction<E, R> propName, Comparable<R> start, Comparable<R> end) {
        list.add(Query.build(Type.BETWEEN, ReflectionUtil.getFieldName(propName), Arrays.asList(start, end)));
        return this;
    }

    public <R> PredicateWrapper<E> between(String propName, Comparable<R> start, Comparable<R> end) {
        list.add(Query.build(Type.BETWEEN, propName, Arrays.asList(start, end)));
        return this;
    }

    public <R> PredicateWrapper<E> notNull(SerializableFunction<E, R> propName) {
        list.add(Query.build(Type.NOT_NULL, ReflectionUtil.getFieldName(propName)));
        return this;
    }

    public <R> PredicateWrapper<E> notNull(String propName) {
        list.add(Query.build(Type.NOT_NULL, propName));
        return this;
    }

    public <R> PredicateWrapper<E> isNull(SerializableFunction<E, R> propName) {
        list.add(Query.build(Type.IS_NULL, ReflectionUtil.getFieldName(propName)));
        return this;
    }

    public <R> PredicateWrapper<E> isNull(String propName) {
        list.add(Query.build(Type.IS_NULL, propName));
        return this;
    }

    public PredicateWrapper<E> and(PredicateWrapper<E> wrapper) {
        wrappers.add(wrapper);
        return this;
    }

    public PredicateWrapper<E> or(PredicateWrapper<E> wrapper) {
        wrappers.add(wrapper);
        return this;
    }
}
